/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */
#include "Application.h"
#include "GlobalVariables.h"

#include "ClientConfig.h"
#include "InputSystem.h"
#include "NetManager.h"
#include "UISystem.h"
#include "GameSceneManager.h"
#include "SystemUtil.h"
#include "ClientUtil.h"

namespace PQClient
{
	
	void drawWinGDI(HWND hWnd)
	{
		PAINTSTRUCT ps;
		HDC hdc=BeginPaint(hWnd,&ps);

		RECT rect;
		GetClientRect(hWnd, &rect) ;
		SetPolyFillMode(hdc,ALTERNATE);

		SelectObject (hdc, GetStockObject(BLACK_BRUSH)) ;// set color of brush and put into hdc
		Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom) ;

		EndPaint(hWnd,&ps);

		ReleaseDC(hWnd, hdc) ;
	}



	LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
	{
		switch (msg)
		{
		case WM_KEYDOWN:
			switch( wParam ){
			case VK_F1:
				break;
			case VK_F2:
				break;
			case VK_F3:
				break;
			case VK_F5:
				break;
			case VK_F6:
				break;
			}
			break;

		case WM_INPUTLANGCHANGEREQUEST:
			break;
		case WM_INPUTLANGCHANGE:
			break;

		case WM_IME_NOTIFY:
			break;
		case WM_PAINT:
			
			break;
		case WM_IME_COMPOSITION:
			break;

		case WM_IME_SETCONTEXT: 
			break;

		case WM_IME_STARTCOMPOSITION:

			break;
		case WM_IME_ENDCOMPOSITION:
			break;

		case WM_DESTROY:
			PostQuitMessage(0);
			break;

		default:
			break;
		}

		return DefWindowProc(hWnd, msg, wParam, lParam);
	}


	
	Application::Application() : _inited(false),_hInst(0)
	{
		
		//We need to accept login event
		this->acceptEvent(Event::Event_Login_Success);
		this->acceptEvent(Event::Event_Server_Change);
	}

	Application::~Application()
	{
	}


	void Application::initOGRE()
	{
		
		//init ogre
		_root=new Ogre::Root();
		if(!_root->restoreConfig()){
			_root->showConfigDialog();
			_root->saveConfig();
		}
		_root->initialise(false);

		_windowWidth=RenderSystemConfig::getRenderWidth();
		_windowHeight=RenderSystemConfig::getRenderHeigh();

		createWindow();
		

		Ogre::NameValuePairList params;
		params["externalWindowHandle"] = Ogre::StringConverter::toString((unsigned int)_hWnd);
		params["vsync"] = "Yes"; 
		params["gamma"] ="Yes"; 
		params["FSAA"] = "2"; 
		
		_win = _root->createRenderWindow(CLINET_RENDER_WINDOW_NAME, _windowWidth,_windowHeight,false,&params);
		

		defineResource();
	}

	void Application::init(HINSTANCE hInst)
	{
		if(_inited){
			assert("Can only set once!");
		}else{
			_hInst=hInst;
		}

		ClientConfig::getSingleton().init();

		
		configCurrentPath();

		initOGRE();

		//must init sub systems first
		//
		InputSystem::getSingleton().init();
		UISystem::getSingleton().init();
		NetManager::getSingleton().init();


		//GameSceneManager manage all client scenes and update the everything in the scene
		//VERY IMPORTANT!
		GameSceneManager::getSingleton().init();//wrap into a graphic system?
		

		_lastTime=_timer.getMilliseconds();
		srand(_lastTime);

	}

	void Application::createWindow()
	{
		WNDCLASSEX winclass;
        winclass.cbSize = sizeof(WNDCLASSEX);
        winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
        winclass.lpfnWndProc = WindowProc;
        winclass.cbClsExtra = 0;
        winclass.cbWndExtra = 0;
		winclass.hInstance = _hInst;
        winclass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
        winclass.hCursor = LoadCursor(NULL,IDC_ARROW);
		winclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
        winclass.lpszMenuName = NULL;
		winclass.lpszClassName = "WINCLASSPQ3D";
        winclass.hIconSm = LoadIcon(NULL,IDI_APPLICATION);

        RegisterClassEx(&winclass);
        
		_hWnd = CreateWindow("WINCLASSPQ3D", "PQ3D",
			WS_OVERLAPPED,0,0,
			_windowWidth,_windowHeight,NULL,NULL,_hInst,NULL);

        if(_hWnd){
			ShowWindow(_hWnd,SW_SHOWNORMAL);

			drawWinGDI(_hWnd);
			

			UpdateWindow(_hWnd);
		}else{
		}
	}


	void Application::configCurrentPath()
	{
		//std::string path=getCurrentPath();
		
		//setCurrentPath("../../../Game");


		//path=getCurrentPath();

	//SetCurrentDirectory();

	}

	//The resource that the whole system need
	void Application::defineResource()
	{
		Ogre::ConfigFile cf;
		cf.load("resources.cfg");
		Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();

		Ogre::String secName, typeName, archName;
		while(seci.hasMoreElements()){
			secName = seci.peekNextKey();
			Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
			Ogre::ConfigFile::SettingsMultiMap::iterator i;
			for(i = settings->begin(); i != settings->end(); ++i){
				typeName = i->first;
				archName = i->second;
				Ogre::ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
			}
		}

		Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
	}

	int Application::run()
	{
		MSG msg;
		ZeroMemory(&msg, sizeof(MSG));

		while (true){
			if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)){ 
				if (msg.message == WM_QUIT){
					break;
				}
				TranslateMessage(&msg);
				DispatchMessage(&msg);
				continue;
			}

			update();
			render();

		}

		return msg.wParam;

	}

	void Application::update()
	{
		//update time
		Ogre::Real deltaT=(_timer.getMilliseconds()-_lastTime)/1000.0f;
		_lastTime=_timer.getMilliseconds();

		//update input system,must at first
		InputSystem::getSingleton().update(deltaT);

		//update active scene
		GameSceneManager::getSingleton().update(deltaT);
		
		//update network, handle packets
		NetManager::getSingleton().update(deltaT);

		UISystem::getSingleton().update(deltaT);

	}

	void Application::render()
	{
		
		Ogre::Root::getSingleton().renderOneFrame();
	}

	//maybe obsolete...
	void Application::handleEvent(Event* evt)
	{
		if(!evt||evt->isHandled()||!evt->sender()){
			return;
		}

		Object* sender=evt->sender();
		switch(evt->type()){
		case Event::Event_Login_Success:
			//GameSceneManager::getSingleton().setActiveSceneId(Scene_CharacterSelect);
			break;
		case Event::Event_Server_Change:
			//GameSceneManager::getSingleton().setActiveSceneId(Scene_ServerChange);
			break;
		default:
			break;
		}
	}

}
